import json
import os
import sys
from fastapi import  APIRouter,Response,Request,Query,BackgroundTasks
from fastapi.responses import JSONResponse
import subprocess
import yaml
import asyncio
import multiprocessing
import re
from redis.asyncio import Redis  # 从redis.asyncio导入异步客户端
topology_router = APIRouter()

def shell_cmd(command):
        # 构造管道命令（注意：awk的单引号需转义，或用双引号包裹整个命令）
    try:
        # 执行命令：shell=True让shell处理管道，text=True让输出为字符串
        result = subprocess.run(
            command,
            shell=True,
            stdout=subprocess.PIPE,  # 捕获标准输出（状态）
            stderr=subprocess.PIPE,  # 捕获标准错误（错误信息）
            text=True,               # 输出为字符串（而非字节）
            timeout=10               # 超时时间（避免命令挂起）
        )
        if result.returncode == 0:
            status = result.stdout.strip()
            return status
        else:
            # 命令执行失败（如qm出错）
            error_msg = result.stderr.strip()
            return f"命令执行失败：{error_msg}"
    except subprocess.TimeoutExpired:
        return "错误：命令执行超时"
    except Exception as e:
        return f"未知错误：{str(e)}"


def mac_add(n):
    k=n%1000
    if k ==0:
        k=1000
    return k
def increment_mac(mac_address,netnum):
    mac_clean = mac_address.replace(':', '')
    if len(mac_clean) != 12 or not all(c in '0123456789ABCDEFabcdef' for c in mac_clean):
        raise ValueError("无效的MAC地址格式，正确格式应为XX:XX:XX:XX:XX:XX")
    mac_int = int(mac_clean, 16)
    mac_int += netnum
    new_mac_clean = format(mac_int, '012X')
    new_mac = ':'.join([new_mac_clean[i:i + 2] for i in range(0, 12, 2)])
    return new_mac



def increment_mac_keep_suffix(mac, step):
    parts = mac.split(':')
    if len(parts) != 6:
        raise ValueError("无效的MAC地址格式，应为xx:xx:xx:xx:xx:xx")
    suffix = parts[-2:]
    prefix_parts = parts[:-2]
    prefix_str = ''.join(prefix_parts)
    prefix_int = int(prefix_str, 16)
    new_prefix_int = prefix_int + step
    if new_prefix_int > 0xFFFFFFFF:
        raise ValueError("MAC地址前缀超出范围")
    new_prefix_hex = f"{new_prefix_int:08x}"
    new_prefix_parts = [
        new_prefix_hex[0:2],
        new_prefix_hex[2:4],
        new_prefix_hex[4:6],
        new_prefix_hex[6:8]
    ]
    new_parts = new_prefix_parts + suffix
    return ':'.join(new_parts)








#获取拓扑
@topology_router.get("/topology/getTopology")
async def get_topology(topologyId:str=Query(...)):
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        allnodes = await redis.hget(f'{topologyId}_topo_allnodes','allnodes')
        alledges = await redis.hget(f'{topologyId}_topo_alledges','alledges')
        #zrange('my_sorted_set', 0, -1)升序
        Available_vmidlist = await redis.zrange(f'{topologyId}_vmid',0,-1)
        Available_interface_list = await redis.zrange(f'{topologyId}_bridgeid',0,-1)
        # [allnodes,alledges]=await redis.hmget(f'{topologyId}_topo','allnodes','alledges')
        json_data = {"allnodes": json.loads(allnodes), "alledges": json.loads(alledges), "Available_vmidlist": Available_vmidlist,'Available_interface_list':Available_interface_list}
    return JSONResponse(content=json_data,status_code=200)
#更新拓扑
@topology_router.post("/topology/update")
async def post_addDevice(request: Request,data:dict,topologyId:str=Query(...)):
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        allnodes = data['allnodes']
        alledges = data['alledges']
        await redis.hset(f'{topologyId}_topo_allnodes','allnodes',json.dumps(allnodes))
        await redis.hset(f'{topologyId}_topo_alledges','alledges',json.dumps(alledges))
    return Response(status_code=200,content='success')

def mul_add_devices(data,topologyId):
    asyncio.run(add_device_task(data,topologyId))
async def add_device_task(data,topologyId):
    deviceversion = data['properties']['device']#根据128T-1这个模板来创建设备
    device = deviceversion.split('-')[0]#128T
    vmid = data['properties']['zIndex']
    bridge_type = data['properties']['bridge_type'].lower()
    mac = "00:10:00:00:00:01"
    step = mac_add(vmid)
    mac = increment_mac_keep_suffix(mac, step)
    Console = data['properties']['Console']
    # #{'id': 'GikdvjslJtkvoFyCgydf1', 'type': 'UserTask', 'x': 156, 'y': 336, 'resizable': False, 'properties': {'device': 'Ruijieswitch', 'width': 56, 'height': 71, 'name': 'R1', 'imgsrc': './images/icon/Router-2D-Gen-Grey-S.svg', 'status': 'down', 'ishovered': '', 'zIndex': 4, 'cpu': 4, 'ram': 4096, 'Ethernet': ['G0/0', 'G0/1', 'G0/2', 'G0/3', 'G0/4', 'G0/5', 'G0/6', 'G0/7', 'G0/8', 'G0/9', 'n/a'], 'UseEth': [], 'Console': 'telnet', 'link': 'telnet://localhost:32768'}}
    #{"name": "zfTwx1JfAjrfqpiATLLZm", "vmid": 1001}, {"name": "ggyPPMSdKOcCfRRQQXrNy", "vmid": 1002}
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        # result = subprocess.run('ls /etc/pve/qemu-server/', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,text=True, timeout=10)
        # qemulist = result.stdout.strip().replace('.conf','').split('\n')
        deviceTemplate = await redis.smembers("device_template")
        deviceTemplate = [json.loads(item) for item in deviceTemplate]
        Template_vmid=''
        for i in deviceTemplate:
            if i["name"] == deviceversion:
                Template_vmid = i["vmid"]
                break
        if not Template_vmid:
            return
        BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        vmname = data['id']
        vmdata = {
            'name': vmname,
            'vmid': vmid,
        }
        cmd = f'qm clone {Template_vmid} {vmid} --name {topologyId}-{vmname}'#101是指128T-1这个模板对应的vmid
        shell_cmd(cmd)

        #判断cpu、内存、接口数量是否和yml一致，一致的话就不修改，不一致的话就修改
        cpu = int(data['properties']['cpu'])
        ram = int(data['properties']['ram'])
        ethernet = len(data['properties']['Ethernet'])
        configpath=os.path.join(BASE_DIR, f'app/config/yml/{device}.yml')
        with open(configpath, 'r', encoding='utf-8') as f:
            deviceconfig =  yaml.safe_load(f.read())
        cpuconfig = deviceconfig.get("cpu")
        ramconfig = deviceconfig.get("ram")
        
        try:
            ethernetconfig = deviceconfig.get("ethernet")+len(deviceconfig.get("eth_name"))
        except Exception:
            ethernetconfig = deviceconfig.get("ethernet")
        if cpu!= cpuconfig:
            #模板那里就修改好了cpucore
            #cpucore = subprocess.run('nproc', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
            #修改cpu
            # cmd = f'qm set {vmid} --cores {cpu} --affinity 1-{int(cpucore.stdout)-1} --cpuunits 95'
            cmd = f"sed -i 's/^cores: [0-9]\+$/cores: {cpu}/' /etc/pve/qemu-server/{vmid}.conf"
            shell_cmd(cmd)
        if ram != ramconfig:
            if int(ram) >2048:#如果是锐捷的设备，那么修改的时候需要设置最小内存、共享内存
                # cmd = f'qm set {vmid} --memory {ram} --balloon 2048 --shares 2048'
                cmd = f"sed -i 's/^memory: [0-9]\+$/memory: {ram}/' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
                cmd = f"sed -i '$a shares: 2048' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
                cmd = f"sed -i '$a balloon: 2048' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
            else:
                #修改虚拟机cpu配置
                #cmd = f'qm set {vmid} --memory {ram}'
                cmd = f"sed -i 's/^memory: [0-9]\+$/memory: {ram}/' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
        if ethernet<ethernetconfig:#如果网卡数量小于配置文件，那么就删除网卡
            for i in range(ethernetconfig-ethernet):
                cmd = f"sed -i '/^net{ethernetconfig-i-1}:/d' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
            # for i in range(ethernetconfig-ethernet):
            #     cmd = f'qm set {vmid} -delete net{ethernetconfig-i-1} '
            #     shell_cmd(cmd)
        if ethernet>ethernetconfig:#如果网卡数量大于配置文件，那么就新增网卡
            # for i in range(ethernet-ethernetconfig):
            #     cmd = f'qm set {vmid} --net{ethernetconfig+i} model={bridge_type},bridge=vm9999,link_down=1'
            #     shell_cmd(cmd)
            for i in range(ethernet-ethernetconfig):
                # sed -i '/^net8:/a net9: e1000=BC:24:11:XX:XX:XX,bridge=tmpbr0,link_down=1' 1001.conf
                cmd = f"sed -i '/^net{ethernetconfig+i-1}:/a net{ethernetconfig+i}: {bridge_type}=BC:24:11:11:11:11,bridge=tmpbr0,link_down=1' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
        for i in range(ethernet):
            #mac = "00:10:00:01:00:01"
            mac_address = increment_mac(mac,i)
            #sed -i '/^net6:/c net6: virtio=aa:bb:cc:dd:ee:ff,bridge=tmpbr0,link_down=1' 101.conf -i：直接修改文件内容,/^net6:/：匹配以 net6: 开头的行,c：表示替换整行内容,后面的字符串是要替换成的新内容
            cmd = f"sed -i '/^net{i}:/c net{i}: {bridge_type}={mac_address},bridge=tmpbr0,link_down=1' /etc/pve/qemu-server/{vmid}.conf"
            shell_cmd(cmd)
        #追加连接方式根据console类型来进行追加
        if Console == 'vnc':
            vncid = 10000+vmid-5900
            cmd = f"sed -i '$a args: -vnc 0.0.0.0:{vncid}' /etc/pve/qemu-server/{vmid}.conf"
            shell_cmd(cmd)
        if Console == 'telnet':
            telnetid = 10000+vmid
            cmd = f"sed -i '$a args: -serial telnet::{telnetid},server,nowait' /etc/pve/qemu-server/{vmid}.conf"
            shell_cmd(cmd)
        await redis.sadd(f'{topologyId}_map', json.dumps(vmdata))  # 添加数据
        #删除vmid被使用了
        await redis.zrem(f'{topologyId}_vmid',vmid)
#添加一台设备
@topology_router.post("/adddevice")
async def add_device(request: Request,data:dict,background_tasks: BackgroundTasks,topologyId:str=Query(...)):#data是设备信息
    #background_tasks.add_task(add_device_task,data,topologyId)
    multiprocessing.Process(target=mul_add_devices, args=(data,topologyId)).start()#使用多进程比BackgroundTasks快
    return Response(status_code=200)

    
async def start_device_task(data,topologyId):
    vmname = data['id']
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        vmid = [item['vmid'] for item in topo_list if item['name'] == vmname][0]
    cmd = f'qm start {vmid}'
    shell_cmd(cmd)
@topology_router.post("/startNode")
async def start_device(request: Request,data:dict,background_tasks: BackgroundTasks,topologyId:str=Query(...)):
    background_tasks.add_task(start_device_task,data,topologyId)
    return Response(status_code=200)

def mul_stop_devices(data,topologyId):
    asyncio.run(stop_device_task(data,topologyId))
async def stop_device_task(data,topologyId):
    vmname = data['id']
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        vmid = [item['vmid'] for item in topo_list if item['name'] == vmname][0]
    #关机机不用qm stop，查询pid，然后kill
    #cmd = f'qm stop {vmid}'
    cmd = f'ps aux | grep {topologyId}-{vmname}'#先查询释放运行中
    result = subprocess.run(
        cmd,
        shell=True,
        stdout=subprocess.PIPE,  # 捕获标准输出（命令输出）
        stderr=subprocess.PIPE,  # 捕获标准错误（错误信息）
        text=True,               # 输出为字符串（而非字节）
        timeout=10               # 超时时间（避免命令挂起）
    )
    r = result.stdout.split('\n')
    for i in r:
        if '/usr/bin/kvm' in i:
            pid = i.split()[1]
            cmd = f'kill {pid}'
            shell_cmd(cmd)
            break
@topology_router.post("/stopNode")
async def stop_device(request: Request,data:dict,background_tasks: BackgroundTasks,topologyId:str=Query(...)):
    multiprocessing.Process(target=mul_stop_devices, args=(data,topologyId)).start()
    #background_tasks.add_task(stop_device_task,data,topologyId)
    return Response(status_code=200)

async def change_device_task(data,topologyId):
    vmname = data['id']
    cpu = data['cpu']
    ram = data['ram']
    device = data['device']
    Console = data['Console']
    #需要判断是否是锐捷设备，如果是锐捷设备，那么需要设置cpu绑定、最小内存和共享内存
    #BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))
    #topo_path  = os.path.join(BASE_DIR, f'app/Labs/{topologyId}/topo.json')
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        vmid = [item['vmid'] for item in topo_list if item['name'] == vmname][0]

        allnodes = await redis.hget(f'{topologyId}_topo_allnodes','allnodes')
        json_data = json.loads(allnodes)
    #with open(topo_path, 'r') as f:
        #json_data = json.load(f)
    #[item['vmid'] for item in json_data['allnodes'] if item['name'] == vmname]
    #判断cpu和ram和原来的值是否一致
    #{"allnodes": [{"id": "EQ5BQ4HsYcobvH6n97vge", "type": "UserTask", "x": 471, "y": 371, "properties": {"device": "128T-1", "startmethod": "", "width": 53, "height": 68, "name": "R1", "imgsrc": "./images/icon/SDWAN-2D-Juniper-SSR-S.svg", "status": "down", "ishovered": "", "zIndex": 0, "cpu": 4, "ram": 8192, "Ethernet": ["ens2", "ens3", "ens4", "ens5", "ens6", "ens7", "ens8", "ens9"], "UseEth": [], "Console": "telnet", "link": "telnet://localhost:32770"}}], "alledges": []}
    for item in json_data:
        if item['id'] == vmname:
            if item['properties']['ram'] != ram:
                if int(ram) >2048:#如果是锐捷的设备，那么修改的时候需要设置cpu绑定、最小内存、共享内存
                    #cmd = f'qm set {vmid} --memory {ram} --balloon 2048 --shares 2048'
                    cmd = f"sed -i 's/^memory: [0-9]\+$/memory: {ram}/' /etc/pve/qemu-server/{vmid}.conf"
                    shell_cmd(cmd)
                    cmd = f"sed -i '$a shares: 2048' /etc/pve/qemu-server/{vmid}.conf"
                    shell_cmd(cmd)
                    cmd = f"sed -i '$a balloon: 2048' /etc/pve/qemu-server/{vmid}.conf"
                    shell_cmd(cmd)
                else:
                    #修改虚拟机cpu配置
                    #cmd = f'qm set {vmid} --memory {ram}'
                    cmd = f"sed -i 's/^memory: [0-9]\+$/memory: {ram}/' /etc/pve/qemu-server/{vmid}.conf"
                    shell_cmd(cmd)
            if item['properties']['cpu'] != cpu:
                    #cpucore = subprocess.run('nproc', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=10)
                    #cmd = f'qm set {vmid} --cores {cpu} --affinity 1-{int(cpucore.stdout)-1} --cpuunits 95'
                    cmd = f"sed -i 's/^cores: [0-9]\+$/cores: {cpu}/' /etc/pve/qemu-server/{vmid}.conf"
                    shell_cmd(cmd)
            if item['properties']['Console'] != Console:
                vncid = 10000+vmid-5900
                cmd = f"sed -i 's/args: -serial telnet::[0-9]\+,server,nowait/args: -vnc 0.0.0.0:{vncid}/' /etc/pve/qemu-server/{vmid}.conf"
                shell_cmd(cmd)
            break
@topology_router.post("/changeNode")
async def change_device(request: Request,data:dict,background_tasks: BackgroundTasks,topologyId:str=Query(...)):
    background_tasks.add_task(change_device_task,data,topologyId)
    return Response(status_code=200)


def mul_delete_device(data,topologyId):
    asyncio.run(delete_device_task(data,topologyId))
async def delete_device_task(data,topologyId):
    vmname = data['currnetNode']
    otherNode = data['otherNode']#需要判断是否是空数组，空数组的话就不需要操作有些内容
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topo_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topo_set)]
        vmid = [item['vmid'] for item in topo_list if item['name'] == vmname][0]
        #qm destroy <VMID> --purge 
        #cmd = f'qm destroy {vmid} --purge'
        #直接删除.conf文件和image文件
        cmd = f'rm -rf /etc/pve/qemu-server/{vmid}.conf'
        shell_cmd(cmd)
        cmd = f'rm -rf /data/sda/images/{vmid}'
        shell_cmd(cmd)
        #删除其他节点的边
        #删除映射的网桥，回收bridgeid
        for i in otherNode:
            tag=''.join(sorted([vmname, i['otherNodeid']]))
            tnodeid =i['otherNodeid']
            otherNodetype = i['otherNodetype']#需要判断是否是网桥还是设备，做对应的操作
            tnode_Ethernet = i['otherNode_Ethernet']
            try:
                otherNode_Status = i['otherNode_Status']
            except KeyError:
                otherNode_Status = 'down'
            #获取tnode对应的vmid
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            if otherNodetype == 'UserTask':
                tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]
                for index,j in enumerate(i['interfaceid']):#操作接口和网桥
                    interfaceid = j
                    k = i['otherNodeEth'][index]['id']
                    teth = i['otherNodeEth'][index][k]    
                    map = {'name':tag,'bridgeid':f'vmbr{interfaceid}'}
                    tnode_net = tnode_Ethernet.index(teth)
                    if otherNode_Status == 'down':
                        unmount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=vmbr{interfaceid}/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/{tnodevmid}.conf"
                        shell_cmd(unmount_tnode_cmd)
                    else:
                        result = subprocess.run(f"cat /etc/pve/qemu-server/{tnodevmid}.conf | grep 'net{tnode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                        netres = result.stdout.strip()
                        pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                        match = re.search(pattern, netres)
                        if match:
                            interface_type = match.group(1)  # 第1组：接口类型
                            mac_address = match.group(2)     # 第2组：MAC地址
                        #获取接口类型、mac地址
                        cmd = f"qm set {tnodevmid} --net{tnode_net} {interface_type}={mac_address},bridge=tmpbr0,link_down=1"
                        shell_cmd(cmd)
                    #删除网桥
                    # # 关闭网桥（设置为DOWN状态）
                    del_bridge_step1 = f'ip link set vmbr{interfaceid} down'
                    shell_cmd(del_bridge_step1)
                    # # 删除网桥（vmbr10）
                    del_bridge_step2 = f'brctl delbr vmbr{interfaceid}'
                    shell_cmd(del_bridge_step2)
                    #删除配置sed -i '/^auto vmbr1$/,/bridge-fd 0$/d' interfaces
                    del_bridge_step3 = f"sed -i '/^auto vmbr{interfaceid}$/,/bridge-fd 0$/d' /etc/network/interfaces"
                    shell_cmd(del_bridge_step3)
                    #删除映射关系
                    await redis.srem(f'{topologyId}_bridgeid_map',json.dumps(map))
                    #在可用的bridgeid表里面添加对应的id
                    await redis.zadd(f'{topologyId}_bridgeid',{str(interfaceid):interfaceid})
            else:#如果对端是网桥，则操作下面内容
                for index,j in enumerate(i['interfaceid']):#操作接口和网桥
                    #删除映射关系
                    interfaceid = j
                    map = {'name':tag,'bridgeid':f'vmbr{interfaceid}'}
                    await redis.srem(f'{topologyId}_bridgeid_map',json.dumps(map))
                    #在可用的bridgeid表里面添加对应的id
                    await redis.zadd(f'{topologyId}_bridgeid',{str(interfaceid):interfaceid})
            #......
        #删除映射的节点，回收vmid
        for i in topo_list:
            
            if i["vmid"] == vmid:
                await redis.srem(f'{topologyId}_map', json.dumps(i))  # 从Set中删除该元素
                #给vmid加回被使用的标记
                await redis.zadd(f'{topologyId}_vmid', {vmid:vmid})
                break  # Set元素唯一，找到后可终止遍历
@topology_router.post("/deleteNode")#删除一台设备
async def delete_device(request: Request,data:dict,background_tasks: BackgroundTasks,topologyId:str=Query(...)):
    #background_tasks.add_task(delete_device_task,data,topologyId)
    multiprocessing.Process(target=mul_delete_device, args=(data,topologyId)).start()
    return Response(status_code=200)

#网桥的操作
@topology_router.post("/addbridge")
async def add_bridge(request: Request,data:dict,topologyId:str=Query(...)):#data是桥信息
    return 'success'
@topology_router.post("/deletebridge")
async def delete_bridge(request: Request,data:dict,topologyId:str=Query(...)):
    vmname = data['currnetNode']
    otherNode = data['otherNodeEth']#需要判断是否是空数组，空数组的话就不需要操作有些内容
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        for i in otherNode:
            tag=''.join(sorted([vmname, i['otherNodeid']]))
            tnodeid =i['otherNodeid']
            tnode_Ethernet = i['otherNode_Ethernet']
            #获取tnode对应的vmid
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]
            for index,j in enumerate(i['interfaceid']):#操作接口和网桥
                    interfaceid = j
                    k = i['otherNodeEth'][index]['id']
                    teth = i['otherNodeEth'][index][k] 
                    tnode_net = tnode_Ethernet.index(teth)
                    unmount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=vmbr[0-9]\+/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/{tnodevmid}.conf"
                    shell_cmd(unmount_tnode_cmd)
    return 'success'
def mul_add_edge(data,topologyId):
    asyncio.run(add_edge_task(data,topologyId))
async def add_edge_task(data,topologyId):
    snodeid = data['snodeid']
    seth = data['seth']
    snode_Ethernet = data['snode_Ethernet']
    tnodeid = data['tnodeid']
    teth = data['teth']
    tnode_Ethernet = data['tnode_Ethernet']
    interfaceid = data['interfaceid']
    tag = ''.join(sorted([snodeid, tnodeid]))
    snode_node_type = data['snode_node_type']
    tnode_node_type = data['tnode_node_type']
    try:
        snode_status = data["snode_status"]
        tnode_status = data["tnode_status"]
    except KeyError:
        snode_status = "down"
        tnode_status = "down"
    map = {'name':tag,'bridgeid':f'vmbr{interfaceid}'}
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        if snode_node_type == "UserTask" and tnode_node_type == "UserTask":#如果对端是设备执行这个逻辑
            #创建网桥，并且2个设备挂接过去
            #写文件
            create_bridge_step1 = "sed -i '$d' /etc/network/interfaces"
            shell_cmd(create_bridge_step1)
            create_bridge_step2  = f'''printf "auto vmbr{interfaceid}\niface vmbr{interfaceid} inet static\n        bridge-ports none\n        bridge-stp off\n        bridge-fd 0\nsource /etc/network/interfaces.d/*\n" >> /etc/network/interfaces'''
            shell_cmd(create_bridge_step2)
            #创建临时网桥
            create_bridge_step3 =f'brctl addbr vmbr{interfaceid}'
            shell_cmd(create_bridge_step3)
            create_bridge_step4 =f'ip link set vmbr{interfaceid} up'
            shell_cmd(create_bridge_step4)
            #修改网桥配置，保障能看见lldp邻居
            create_bridge_step5 = f'echo 16384 > /sys/class/net/vmbr{interfaceid}/bridge/group_fwd_mask'
            shell_cmd(create_bridge_step5)
            #底层命令操作2个设备连在一个bridge上
            #修改设备的接口，挂载到网桥上面
            #先查询redis拿到设备对应的vmid
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            snodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == snodeid][0]
            tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]
            #获取需要修改的接口
            snode_net = snode_Ethernet.index(seth)
            tnode_net = tnode_Ethernet.index(teth)

            if snode_status == "down":
                #挂载接口到网桥上面
                #sed -i '/^net0:/s/bridge=vmbr11111,link_down=1/bridge=vmbr123/' 文件名
                mount_snode_cmd = f"sed -i '/^net{snode_net}:/s/bridge=tmpbr0,link_down=1/bridge=vmbr{interfaceid}/' /etc/pve/qemu-server/{snodevmid}.conf"
                shell_cmd(mount_snode_cmd)
                #挂载tap接口到网桥上面
                mount_snode_cmd = f"ip link set tap{snodevmid}i{snode_net} master vmbr{interfaceid}"
                shell_cmd(mount_snode_cmd)
            else:
                #
                result = subprocess.run(f"cat /etc/pve/qemu-server/{snodevmid}.conf | grep 'net{snode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {snodevmid} --net{snode_net} {interface_type}={mac_address},bridge=vmbr{interfaceid}"
                shell_cmd(cmd)
            if tnode_status == "down":
                mount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=tmpbr0,link_down=1/bridge=vmbr{interfaceid}/' /etc/pve/qemu-server/{tnodevmid}.conf"
                shell_cmd(mount_tnode_cmd)
                mount_tnode_cmd = f"ip link set tap{tnodevmid}i{tnode_net} master vmbr{interfaceid}"
                shell_cmd(mount_tnode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{tnodevmid}.conf | grep 'net{tnode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {tnodevmid} --net{tnode_net} {interface_type}={mac_address},bridge=vmbr{interfaceid}"
                shell_cmd(cmd)
            #添加映射关系
            await redis.sadd(f'{topologyId}_bridgeid_map',json.dumps(map))
            #在可用的bridgeid表里面删除对应的id
            await redis.zrem(f'{topologyId}_bridgeid',str(interfaceid))
        elif snode_node_type == "BridgeNet" and tnode_node_type == "UserTask":
            #如果snode是网桥，则操作下面内容,关注tnode就可以了，snode是网桥，tnode是设备
            #......
            #设备直接挂载到网桥上
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]


            tnode_net = tnode_Ethernet.index(teth)
            snode_net = snode_Ethernet.index(seth)

            if tnode_status == "down":
                mount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=tmpbr0,link_down=1/bridge=vmbr{snode_net}/' /etc/pve/qemu-server/{tnodevmid}.conf"
                shell_cmd(mount_tnode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{tnodevmid}.conf | grep 'net{tnode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {tnodevmid} --net{tnode_net} {interface_type}={mac_address},bridge=vmbr{interfaceid}"
                shell_cmd(cmd)
        else:#源是设备，目的是网桥
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            snodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == snodeid][0]
            snode_net = snode_Ethernet.index(seth)
            tnode_net = tnode_Ethernet.index(teth)

            if snode_status =="down":
                mount_snode_cmd = f"sed -i '/^net{snode_net}:/s/bridge=tmpbr0,link_down=1/bridge=vmbr{tnode_net}/' /etc/pve/qemu-server/{snodevmid}.conf"
                shell_cmd(mount_snode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{snodevmid}.conf | grep 'net{snode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {snodevmid} --net{snode_net} {interface_type}={mac_address},bridge=vmbr{interfaceid}"
                shell_cmd(cmd)
#链路操作
@topology_router.post("/addedge")
async def add_edge(request: Request,data:dict,topologyId:str=Query(...)):
    multiprocessing.Process(target=mul_add_edge, args=(data,topologyId)).start()#使用多进程比BackgroundTasks快
    return Response(status_code=200)
def mul_del_edge(data,topologyId):
    asyncio.run(delete_Edge_task(data,topologyId))
async def delete_Edge_task(data,topologyId):
    interfaceid = data['interfaceid']
    edgeid=data['edgeid']
    snodeid = data['snode']
    tnodeid = data['tnode']
    seth = data['startLabel']
    teth = data['endLabel']
    snode_Ethernet = data['snode_Ethernet']
    tnode_Ethernet = data['tnode_Ethernet']
    snode_type = data['snode_type']
    tnode_type = data['tnode_type']
    tag = ''.join(sorted([snodeid, tnodeid]))
    try:
        snode_status=data["snode_status"]
        tnode_status=data["tnode_status"]
    except KeyError:
        snode_status='down'
        tnode_status='down'
    map = {'name':tag,'bridgeid':f'vmbr{interfaceid}'}
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        #修改设备的接口，挂载到网桥上面
        #先查询redis拿到设备对应的vmid
        if snode_type =='UserTask':
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            snodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == snodeid][0]
            tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]
            #获取需要修改的接口
            snode_net = snode_Ethernet.index(seth)
            tnode_net = tnode_Ethernet.index(teth)
            if snode_status=="down":
                #sed -i '/^net1:/s/bridge=vmbr1002/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/1001.conf
                unmount_snode_cmd = f"sed -i '/^net{snode_net}:/s/bridge=vmbr{interfaceid}/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/{snodevmid}.conf"
                shell_cmd(unmount_snode_cmd)
                #卸载tap接口到网桥上面
                unmount_snode_cmd = f"ip link set tap{snodevmid}i{snode_net} master tmpbr0"
                shell_cmd(unmount_snode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{snodevmid}.conf | grep 'net{snode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {snodevmid} --net{snode_net} {interface_type}={mac_address},bridge=vmbr{interfaceid},link_down=1"
                shell_cmd(cmd)

            if tnode_status=="down":
                unmount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=vmbr{interfaceid}/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/{tnodevmid}.conf"
                shell_cmd(unmount_tnode_cmd)
                #卸载tap接口到网桥上面
                unmount_tnode_cmd = f"ip link set tap{tnodevmid}i{tnode_net} master tmpbr0"
                shell_cmd(unmount_tnode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{tnodevmid}.conf | grep 'net{tnode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {tnodevmid} --net{tnode_net} {interface_type}={mac_address},bridge=tmpbr0,link_down=1"
                shell_cmd(cmd)
            #删除网桥
            # # 关闭网桥（设置为DOWN状态）
            del_bridge_step1 = f'ip link set vmbr{interfaceid} down'
            shell_cmd(del_bridge_step1)
            # # 删除网桥（vmbr10）
            del_bridge_step2 = f'brctl delbr vmbr{interfaceid}'
            shell_cmd(del_bridge_step2)
            #删除配置sed -i '/^auto vmbr1$/,/bridge-fd 0$/d' interfaces
            del_bridge_step3 = f"sed -i '/^auto vmbr{interfaceid}$/,/bridge-fd 0$/d' /etc/network/interfaces"
            shell_cmd(del_bridge_step3)
            #删除映射关系
            await redis.srem(f'{topologyId}_bridgeid_map',json.dumps(map))
            #在可用的bridgeid表里面添加对应的id
            await redis.zadd(f'{topologyId}_bridgeid',{str(interfaceid):interfaceid})
        else:
            #....
            map_list = list(await redis.smembers(f'{topologyId}_map'))
            tnodevmid = [json.loads(item)["vmid"] for item in map_list if json.loads(item)["name"] == tnodeid][0]
            tnode_net = tnode_Ethernet.index(teth)
            snode_net = snode_Ethernet.index(seth)
            if tnode_status=="down":
                unmount_tnode_cmd = f"sed -i '/^net{tnode_net}:/s/bridge=vmbr{snode_net}/bridge=tmpbr0,link_down=1/' /etc/pve/qemu-server/{tnodevmid}.conf"
                shell_cmd(unmount_tnode_cmd)
            else:
                result = subprocess.run(f"cat /etc/pve/qemu-server/{tnodevmid}.conf | grep 'net{tnode_net}:' ",stdout=subprocess.PIPE,shell=True,text=True)
                netres = result.stdout.strip()
                pattern = r'(\w+)=([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})'
                match = re.search(pattern, netres)
                if match:
                    interface_type = match.group(1)  # 第1组：接口类型
                    mac_address = match.group(2)     # 第2组：MAC地址
                #获取接口类型、mac地址
                cmd = f"qm set {tnodevmid} --net{tnode_net} {interface_type}={mac_address},bridge=tmpbr0,link_down=1"
                shell_cmd(cmd)
@topology_router.post("/deleteEdge")
async def delete_Edge(request: Request,data:dict,topologyId:str=Query(...)):
    multiprocessing.Process(target=mul_del_edge, args=(data,topologyId)).start()#使用多进程比BackgroundTasks快
    return Response(status_code=200)


@topology_router.post("/topology/NodesStatus")
async def topology_NodesStatus(request: Request,data:dict,topologyId:str=Query(...)):
    node_status = []
    nodelist = data['nodes']
    resultlist = []
    async with Redis.from_url(url="redis://localhost:6379/0",decode_responses=True) as redis:
        topp_set = await redis.smembers(f'{topologyId}_map')
        topo_list = [json.loads(item) for item in list(topp_set)]
        cmd = "qm list"
        result = subprocess.run(cmd,stdout=subprocess.PIPE,shell=True,text=True)
        data_str = result.stdout.strip()
        for line in data_str.splitlines()[1:]:
            nodename = line.split()[1]
            if f'{topologyId}-' in nodename:
                lname=nodename.split('-')[1]
            else:
                lname=nodename
            node_status.append({"vmid":int(line.split()[0]),"vmname":lname,"status":line.split()[2]})
        # 构建一个vmname到node_status的字典方便查找
        node_status_dict = {ns['vmname']: ns for ns in node_status}
        for topo in topo_list:
            node = node_status_dict.get(topo['name'])
            if node:
                # 匹配则返回vmid
                resultlist.append(node)
            else:
                # 不匹配则返回deleted状态（你指定的返回结构）
                resultlist.append({'vmid': topo['vmid'], 'vmname': topo['name'], 'status': 'deleted'})  
    return resultlist